//----------------------------------------------------------------------------
// File:	bmp2c.cpp
// Author:	Tony Saveski
// Notes:	Converts a 24-bit BMP file into C code for a 'compiled bitmap'.
//----------------------------------------------------------------------------
#include "defines.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//----------------------------------------------------------------------------
#ifdef WIN32
#pragma pack(push)
#pragma pack(1)
#endif

typedef struct
{
	char	sig[2];			// 'BM'
	uint32	file_size;		// File size in bytes
	uint32	reserved;		// unused (=0)
	uint32	data_offset;	// File offset to Raster Data
} bmp_header_t;

typedef struct
{
	uint32 size;			// Size of InfoHeader = 40
	uint32 width;			// Bitmap Width
	uint32 height;			// Bitmap Height
	uint16 planes;			// Number of Planes = 1
	uint16 bpp;				// Bits per Pixel = 24
	uint32 comp;			// Type of Compression = 0
	uint32 image_size;		// (compressed) Size of Image
	uint32 x_pixels_per_m;	// horizontal resolution: Pixels/meter
	uint32 y_pixels_per_m;	// vertical resolution: Pixels/meter
	uint32 colors_used;		// Number of actually used colors
	uint32 colors_important;// Number of important colors
} bmp_iheader_t;

#ifdef WIN32
#pragma pack(pop)
#endif

//----------------------------------------------------------------------------
static bmp_header_t		bmp_h, bmp_h2;
static bmp_iheader_t	bmp_ih, bmp_ih2;

//----------------------------------------------------------------------------
void usage(void)
{
	fprintf(stderr, "\n");
	fprintf(stderr, "usage: bmp2c <bmp file> <c name> [alpha file]\n");
	exit(1);
}

//----------------------------------------------------------------------------
int main(int argc, char **argv)
{
char	filename[512], varname[256], alphaname[512];
FILE	*fh=NULL, *fh2=NULL;
uint16	w, h;
uint16	line_len;
uint8	*line=NULL, *p=NULL;
uint8	*line2=NULL, *p2=NULL;
uint16	i, j;
uint32	out;
uint8	alpha;

	if(argc < 3) usage();

	strcpy(filename, argv[1]);
	strcpy(varname, argv[2]);

	alphaname[0]=0;
	if(argc == 4) strcpy(alphaname, argv[3]);


	// open both files
	fh = fopen(filename, "rb");
	if(!fh)
	{
		fprintf(stderr, "ERROR: failed to load file:'%s'\n", filename);
		goto err_bmp2c;
	}

	if(alphaname[0])
	{
		fh2 = fopen(alphaname, "rb");
		if(!fh2)
		{
			fprintf(stderr, "ERROR: failed to load file:'%s'\n", alphaname);
			goto err_bmp2c;
		}
	}

	// read the header from each file and verify
	if(!fread(&bmp_h, sizeof(bmp_header_t), 1, fh))
	{
		fprintf(stderr, "ERROR: failed to read bmp_header_t structure from file:'%s'\n", filename);
		goto err_bmp2c;
	}

	if(!fread(&bmp_ih, sizeof(bmp_iheader_t), 1, fh))
	{
		fprintf(stderr, "ERROR: failed to read bmp_iheader_t structure from file:'%s'\n", filename);
		goto err_bmp2c;
	}

	if( (bmp_h.sig[0]  != 'B') ||
		(bmp_h.sig[1]  != 'M') ||
		(bmp_ih.size   != 40)  ||
		(bmp_ih.planes != 1)   ||
		(bmp_ih.bpp    != 24)  ||
		(bmp_ih.comp   != 0))
	{
		fprintf(stderr, "ERROR: only valid 24-bit BMP files are supported by this tool\n");
		goto err_bmp2c;
	}

	if(alphaname[0])
	{
		if(!fread(&bmp_h2, sizeof(bmp_header_t), 1, fh2))
		{
			fprintf(stderr, "ERROR: failed to read bmp_header_t structure from file:'%s'\n", alphaname);
			goto err_bmp2c;
		}

		if(!fread(&bmp_ih2, sizeof(bmp_iheader_t), 1, fh2))
		{
			fprintf(stderr, "ERROR: failed to read bmp_iheader_t structure from file:'%s'\n", alphaname);
			goto err_bmp2c;
		}

		if( (bmp_h2.sig[0]  != 'B') ||
			(bmp_h2.sig[1]  != 'M') ||
			(bmp_ih2.size   != 40)  ||
			(bmp_ih2.planes != 1)   ||
			(bmp_ih2.bpp    != 24)  ||
			(bmp_ih2.comp   != 0))
		{
			fprintf(stderr, "ERROR: only valid 24-bit BMP files are supported by this tool\n");
			goto err_bmp2c;
		}
	}

	// some useful calcs
	w = bmp_ih.width;
	h = bmp_ih.height;
	line_len = w*3;

	// allocate the two bitmap line buffers
	line = (uint8 *)malloc(line_len);
	if(!line) goto err_bmp2c;

	if(alphaname[0])
	{
		line2 = (uint8 *)malloc(line_len);
		if(!line2) goto err_bmp2c;
	}

	// TODO: check that w and h are powers of 2 and adjust if needed
	fprintf(stdout, "// File Generated by BMP2C from %s\n", filename);
	fprintf(stdout, "unsigned int %s_w = %d;\n", varname, w);
	fprintf(stdout, "unsigned int %s_h = %d;\n", varname, h);
	fprintf(stdout, "\n");
	fprintf(stdout, "unsigned int %s[] = {\n", varname);

	// Read rows in reverse order and print code to stdout
	for(i=0; i<h; i++)
	{
		// read a line from each file
		if(fseek(fh, bmp_h.data_offset+((h-i-1)*line_len), SEEK_SET))
		{
			fprintf(stderr, "ERROR: failed to seek to row %d of image data in file:'%s'\n", i, filename);
			goto err_bmp2c;
		}

		if(!fread(line, line_len, 1, fh))
		{
			fprintf(stderr, "ERROR: failed to read row %d of image data in file:'%s'\n", i, filename);
			goto err_bmp2c;
		}

		if(alphaname[0])
		{
			if(fseek(fh2, bmp_h2.data_offset+((h-i-1)*line_len), SEEK_SET))
			{
				fprintf(stderr, "ERROR: failed to seek to row %d of image data in file:'%s'\n", i, alphaname);
				goto err_bmp2c;
			}

			if(!fread(line2, line_len, 1, fh2))
			{
				fprintf(stderr, "ERROR: failed to read row %d of image data in file:'%s'\n", i, alphaname);
				goto err_bmp2c;
			}
		}
		
		// write the line bitmap data		
		fprintf(stdout, "\t// row %d\n", i);
		p = line; // p[0]=B, p[1]=G, p[2]=R
		p2 = line2; // p[0]=B, p[1]=G, p[2]=R
		for(j=0; j<w; j++, p+=3, p2+=3)
		{
			out = ((uint32)(p[0]) << 16) | ((uint32)(p[1]) << 8) | ((uint32)(p[2]));

			if(alphaname[0])
			{
				alpha = p2[0]; // any of the three is OK.
				fprintf(stdout, "\t0x%02x%06x, ", alpha, out);
			}
			else
			{
				if(out==0)
					fprintf(stdout, "\t0x00%06x, ", out);
				else
					fprintf(stdout, "\t0x80%06x, ", out);
			}

			if(j%5 == 0) fprintf(stdout, "\n");
		}
	}
	fprintf(stdout, "\n};\n");

	free(line);
	fclose(fh);

	if(alphaname[0])
	{
		free(line2);
		fclose(fh2);
	}

	return(0);

err_bmp2c:
	if(line)  free(line);
	if(fh)    fclose(fh);
	if(line2) free(line2);
	if(fh2)   fclose(fh2);
	return(1);
}

